package pseudoAssemblerCompiler.twopassassembler.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pseudoAssemblerCompiler.twopassassembler.common.NotificationCenter;
import pseudoAssemblerCompiler.twopassassembler.model.assemblerAlgorithms.Assembler;
import pseudoAssemblerCompiler.twopassassembler.model.assemblerAlgorithms.IViewModel;
import pseudoAssemblerCompiler.twopassassembler.model.ModelObserver;
import pseudoAssemblerCompiler.twopassassembler.model.codeoperationtable.ICodeOperationTableModel;
import pseudoAssemblerCompiler.twopassassembler.model.referencetable.IReferenceTableModel;
import pseudoAssemblerCompiler.twopassassembler.model.symbolnametable.ISymbolicNamesTableModel;
import pseudoAssemblerCompiler.twopassassembler.view.IMainView;
import pseudoAssemblerCompiler.twopassassembler.view.TPAView;
import pseudoAssemblerCompiler.twopassassembler.view.common.IView;
import pseudoAssemblerCompiler.twopassassembler.view.firstpass.Console;
import pseudoAssemblerCompiler.twopassassembler.view.firstpass.Editor;
import pseudoAssemblerCompiler.twopassassembler.view.firstpass.codeoperationtable.ICodeOperationTableView;
import pseudoAssemblerCompiler.twopassassembler.view.secondpass.IReferenceTableView;
import pseudoAssemblerCompiler.twopassassembler.view.secondpass.ISymbolicNamesTableView;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Данный абстрактный класс представляет базовые методы для каждого контроллера.
 * Регистрирует модели и представления, передаёт сообщения об изменении модели всем
 * зарегистрированным представлениям.
 *
 * @author Администратор
 */
public abstract class AbstractController implements ModelObserver, ControllerInterface {

    private static final Logger LOGGER =
            LoggerFactory.getLogger(AbstractController.class);


    protected IMainView mainView; //Главное окно программы

    protected ICodeOperationTableView codeOperationTableView;

    protected ICodeOperationTableModel codeOperationTableModel;

    protected Editor editor;

    protected Assembler mainModel;

    protected ISymbolicNamesTableModel symbolicNamesTableModel;

    protected IReferenceTableModel referenceTableModel;

    protected Console console;

    protected IReferenceTableView referenceTableView;

    protected ISymbolicNamesTableView symbolicNamesTableView;

    /**
     * Содержит все зарегистрированные модели для данного контроллера.
     */
    protected Set<IViewModel> registeredModels;
    /**
     * Содержит все зарегистрированные представления для данного контроллера.
     */
    private List<IView> _registeredViews;

    /**
     * Создаём новый экземпляр контроллера.
     */
    public AbstractController() {
        registeredModels = new HashSet<IViewModel>();
        _registeredViews = new ArrayList<IView>();
    }

    @Override
    public boolean containsSender(IViewModel sender) {
        return registeredModels.contains(sender);
    }

    /**
     * Связываем модель с данным контроллером. После добавления, контроллер будет
     * слушать все изменения модели и сообщать о них представлениям. Он также отвечает
     * за изменения модели, при совершении каких-либо действий пользователем.
     *
     * @param aModel модель для добавления
     */
    public void addModel(IViewModel aModel) {
        registeredModels.add(aModel);
        if (aModel instanceof ICodeOperationTableModel) {
            codeOperationTableModel = (ICodeOperationTableModel) aModel;
            LOGGER.debug("добавлена модель ТКО {}", codeOperationTableModel);
        }
        if (aModel instanceof Assembler) {
            mainModel = (Assembler) aModel;
            LOGGER.debug("добавлена модель ТКО {}", codeOperationTableModel);
        }
        if (aModel instanceof IReferenceTableModel) {
            referenceTableModel = (IReferenceTableModel) aModel;
            LOGGER.debug("добавлена модель вспомогательной таблицы {}",
                    referenceTableModel);
        }
        if (aModel instanceof ISymbolicNamesTableModel) {
            symbolicNamesTableModel = (ISymbolicNamesTableModel) aModel;
            LOGGER.debug("добавлена модель таблицы символических имён {}",
                    symbolicNamesTableModel);
        }
        /*
         * контроллер наблюдает за моделью
         */
        NotificationCenter.getDefaultCenter().addObserver((ModelObserver) this);
    }

    /**
     * Убираем связь модели с данным контроллером.
     *
     * @param aModel удаляемая модель
     */
    public void removeModel(IViewModel aModel) {
        registeredModels.remove(aModel);
    }

    /**
     * Связываем представление с данным контроллером. Контроллер сообщает об изменения
     * модели, каждому зарегистрированному представлению.
     *
     * @param aView представление для добавления.
     */
    public void addView(IView aView) {
        _registeredViews.add(aView);
        if (aView instanceof ICodeOperationTableView) {
            codeOperationTableView = (ICodeOperationTableView) aView;
            LOGGER.debug("добавлено представление ТКО {}", codeOperationTableView);
        }
        if (aView instanceof Editor) {
            editor = (Editor) aView;
            LOGGER.debug("добавлено представление текстового поля редактирования {}",
                    editor);
        }
        if (aView instanceof Console) {
            console = (Console) aView;
            LOGGER.debug("добавлено представление консоли вывода ошибок {}",
                    console);
        }
        if (aView instanceof IReferenceTableView) {
            referenceTableView = (IReferenceTableView) aView;
            LOGGER.debug("добавлено представление вспомогательной таблицы {}",
                    referenceTableView);
        }
        if (aView instanceof ISymbolicNamesTableView) {
            symbolicNamesTableView = (ISymbolicNamesTableView) aView;
            LOGGER.debug("добавлено представление таблицы символических имен {}",
                    symbolicNamesTableView);
        }
    }

    /**
     * Убираем связь представления с контроллером.
     *
     * @param aViewModel удаляемый контроллер.
     */
    public void removeView(IViewModel aViewModel) {
        _registeredViews.remove(aViewModel);
    }

//    @Override
//    public void firstPass(String aProgramm) {
//        for (IViewModel o : registeredModels) {
//            o.firstPass(aProgramm);
//        }
//    }

    public void updateOperationCodeTable(List<String[]> aTable) {
        System.out.println("updateOperationCodeTable - Controller");
        for (IView o : _registeredViews) {
//            o.updateOperationCodeTable(aTable);
        }
    }

    public void updateSecondPassTable(List<String[]> aTable) {
        System.out.println("updateSecondPassTable - AbstractModel");
        for (IView o : _registeredViews) {
//            o.updateSecondPassTable(aTable);
        }
    }

    /**
     * Если в контроллере есть интерфейс для работы с таблицей кодов операций,
     * то возвращаем её
     *
     * @return модель таблицы кодов операций
     */
    protected ICodeOperationTableModel getCodeOperationTable() {
        ICodeOperationTableModel iCodeOperationTableModel = null;
        for (IViewModel o : registeredModels) {
            if (o instanceof ICodeOperationTableModel) {
                iCodeOperationTableModel = (ICodeOperationTableModel) o;
            }
        }
        return iCodeOperationTableModel;
    }

    public void addMainView(TPAView view) {
        mainView = view;
    }

}
